﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Audio;

namespace Gliese581g
{
    public enum UnitType
    {
        Commander = 1,
        Infantry = 2,
        Scout = 3,
        RoughRider = 4,
        Tank = 5,
        Artillery = 6,
        Mech = 7
    }


    // Unit needs to inherit from ClickableSprite especially for Animation support.
    public class Unit : ClickableSprite
    {
        public class UnitFactory
        {
            public static Unit MakeUnit(UnitType type)
            {
                switch (type)
                {
                    case UnitType.Commander:
                        return MakeCommander("");
                    case UnitType.Infantry:
                        return MakeInfantry();
                    case UnitType.Scout:
                        return MakeScout();
                    case UnitType.RoughRider:
                        return MakeRoughRider();
                    case UnitType.Tank:
                        return MakeTank();
                    case UnitType.Artillery:
                        return MakeArtillery();
                    case UnitType.Mech:
                        return MakeMech();
                    default:
                        throw new Exception("invalid unit type");
                }
            }

            static int infantry_id = 0;
            public static Unit MakeInfantry()
            {
                Unit infantry = new Unit(
                    UnitType.Infantry,
                    "Infantry Battalion " + infantry_id++, //name
                    TextureStore.Get(TexId.unit_infantry), //texture
                    new RangeTemplate(4, false, false, true),  //move
                    new RangeTemplate(1, true, true, false), //target template
                    new LineTemplate(1, false, false, true),
                    new UnitDamageEffect(30, false), //damage
                    70, //hp
                    3, //recharge
                    SfxStore.Get(SfxId.infantry_selected_1),
                    SfxStore.Get(SfxId.infantry_fire));
                infantry.SfxSelected.Add(SfxStore.Get(SfxId.infantry_selected_2));
                infantry.SfxMove.Add(SfxStore.Get(SfxId.infantry_move_1));
                infantry.SfxMove.Add(SfxStore.Get(SfxId.infantry_move_2));
                infantry.SfxKilled.Add(SfxStore.Get(SfxId.infantry_killed));
                return infantry;
            }

            static int scout_id = 0;
            public static Unit MakeScout()
            {
                Unit scout = new Unit(
                    UnitType.Scout,
                    "Scout Division" + scout_id++, //name
                    TextureStore.Get(TexId.unit_scout), //texture
                    new RangeTemplate(5, false, false, true),  //move
                    new RangeTemplate(1, true, true, false),  //attack range
                    new LineTemplate(1, false, false, true),
                    new UnitDamageEffect(20, false), //damage
                    55, //hp
                    2, //recharge
                    SfxStore.Get(SfxId.scout_selected_1),
                    SfxStore.Get(SfxId.infantry_fire));
                scout.SfxSelected.Add(SfxStore.Get(SfxId.scout_selected_2));
                scout.SfxMove.Add(SfxStore.Get(SfxId.scout_move_1));
                scout.SfxMove.Add(SfxStore.Get(SfxId.scout_move_2));
                scout.SfxKilled.Add(SfxStore.Get(SfxId.scout_killed));
                return scout;
            }

            static int roughrider_id = 0;
            public static Unit MakeRoughRider()
            {
                Unit roughrider = new Unit(
                    UnitType.RoughRider,
                    "Rough Rider Division" + roughrider_id++, //name
                    TextureStore.Get(TexId.unit_roughrider), //texture
                    new RangeTemplate(4, false, false, true),  //move
                    new RangeTemplate(3, true, true, false),  //attack range
                    new LineTemplate(1, false, false, true),
                    new UnitDamageEffect(25, false), //damage
                    65, //hp
                    3, //recharge
                    SfxStore.Get(SfxId.roughrider_selected_1),
                    SfxStore.Get(SfxId.rocket_boom));
                roughrider.SfxSelected.Add(SfxStore.Get(SfxId.roughrider_selected_2));
                roughrider.SfxMove.Add(SfxStore.Get(SfxId.roughrider_move_1));
                roughrider.SfxMove.Add(SfxStore.Get(SfxId.roughrider_move_2));
                roughrider.SfxKilled.Add(SfxStore.Get(SfxId.roughrider_killed));
                return roughrider;
            }


            static int tank_id = 0;
            public static Unit MakeTank()
            {
                Unit tank = new Unit(
                    UnitType.Tank,
                    "Armor Battalion " + tank_id++, //name
                    TextureStore.Get(TexId.unit_tank), //texture
                    new RangeTemplate(3, false, false, true),  //move
                    new LineTemplate(2, false, false, false),  //attack range
                    new LineTemplate(3, true, true, true),
                    new UnitDamageEffect(60, false), //damage
                    110, //hp
                    5, //recharge
                    SfxStore.Get(SfxId.tank_selected_1),
                    SfxStore.Get(SfxId.tank_fire));
                tank.SfxSelected.Add(SfxStore.Get(SfxId.tank_selected_2));
                tank.SfxMove.Add(SfxStore.Get(SfxId.tank_move_1));
                tank.SfxMove.Add(SfxStore.Get(SfxId.tank_move_2));
                tank.SfxKilled.Add(SfxStore.Get(SfxId.tank_killed));
                return tank;
            }

   
            public static Unit MakeCommander(string playerName)
            {
                Unit commander = new Unit(
                    UnitType.Commander,
                    "Commander " + playerName, //name
                    TextureStore.Get(TexId.unit_commander), //texture
                    new RangeTemplate(3, false, false, true),  //move
                    new LineTemplate(2, false, false, false), //target template
                    new LineTemplate(5, false, false, true),
                    new UnitDamageEffect(50, true), //damage
                    140, //hp
                    5,//recharge
                    SfxStore.Get(SfxId.commander_selected_1),
                    SfxStore.Get(SfxId.mech_fire));
                commander.SfxSelected.Add(SfxStore.Get(SfxId.commander_selected_2));
                commander.SfxMove.Add(SfxStore.Get(SfxId.commander_move_1));
                commander.SfxMove.Add(SfxStore.Get(SfxId.commander_move_2));
                commander.SfxKilled.Add(SfxStore.Get(SfxId.commander_killed));

                commander.IsCommander = true; // need to set the commander flag
                return commander;
            }

            public static Unit MakeMech()
            {
                Unit mech = new Unit(
                    UnitType.Mech,
                    "Techno-mech Guardian", //name
                    TextureStore.Get(TexId.unit_mech), //texture
                    new RangeTemplate(3, false, false, true),  //move
                    new LineTemplate(2, false, false, false), //target template
                    new LineTemplate(5, false, false, true),
                    new UnitDamageEffect(55, true), //damage
                    200, //hp
                    6, //recharge
                    SfxStore.Get(SfxId.mech_selected_1),
                    SfxStore.Get(SfxId.mech_fire));
                mech.SfxSelected.Add(SfxStore.Get(SfxId.mech_selected_2));
                mech.SfxMove.Add(SfxStore.Get(SfxId.mech_move_1));
                mech.SfxMove.Add(SfxStore.Get(SfxId.mech_move_2));
                mech.SfxKilled.Add(SfxStore.Get(SfxId.mech_killed));
                return mech;
            }


            static int artillery_id = 0;
            public static Unit MakeArtillery()
            {
                Unit artillery = new Unit(
                    UnitType.Artillery,
                    "Artillery Battalion" + artillery_id++, //name
                    TextureStore.Get(TexId.unit_artillery), //texture
                    new RangeTemplate(2, false, false, true),  //move
                    new RangeTemplate(4, true, true, false),  //attack range
                    new RangeTemplate(1, true, true, true),
                    new UnitDamageEffect(35, true), //damage
                    80, //hp
                    5,//recharge
                    SfxStore.Get(SfxId.artillery_selected_1),
                    SfxStore.Get(SfxId.artillery_fire));
                artillery.SfxSelected.Add(SfxStore.Get(SfxId.artillery_selected_2));
                artillery.SfxMove.Add(SfxStore.Get(SfxId.artillery_move_1));
                artillery.SfxMove.Add(SfxStore.Get(SfxId.artillery_move_2));
                artillery.SfxKilled.Add(SfxStore.Get(SfxId.artillery_killed));
                return artillery;
            }

        }
        //-------------------------------------------------------------------
        




        protected Player m_owner;
        public Player Owner
        {
            get { return m_owner; }
            set
            {
                m_owner = value;
                Tint = value.UnitColor;
            }
        }

        public UnitType TypeOfUnit;
        public string Name;
        public MapTemplate MoveTemplate;

        // Things that define the attack.   
        public MapTemplate TargetTemplate;
        public MapTemplate AttackTemplate;
        public HexEffect AttackEffect; 


        public int MaxHP;
        public int CurrentHP;
        public bool IsCommander;

        public int MaxRechargeTime;
        public int CurrentRechargeTime;


        //Sounds
        List<SoundEffect> SfxSelected = new List<SoundEffect>();
        List<SoundEffect> SfxMove = new List<SoundEffect>();
        List<SoundEffect> SfxFire = new List<SoundEffect>();
        List<SoundEffect> SfxKilled = new List<SoundEffect>();

        public TimeSpan PlaySfxSelected()
        { return PlayRandomSoundFromListIfVoiceEnabled(SfxSelected); }

        public TimeSpan PlaySfxMove()
        { return PlayRandomSoundFromListIfVoiceEnabled(SfxMove); }

        public TimeSpan PlaySfxFire()
        { return PlayRandomSoundFromListIfVoiceEnabled(SfxFire); }

        public TimeSpan PlaySfxKilled()
        { return PlayRandomSoundFromListIfVoiceEnabled(SfxKilled); } 


        public static TimeSpan PlayRandomSoundFromListIfVoiceEnabled(List<SoundEffect> list)
        {
            if (!ConfigManager.GlobalManager.UnitVoicesEnabled || list.Count <= 0)
            {
                SfxStore.Get(SfxId.thump).Play(ConfigManager.GlobalManager.SfxVolume, 0f, 0f);

                return SfxStore.Get(SfxId.thump).Duration;
            }
            SoundEffect soundToPlay = list[new Random().Next(list.Count)];
            soundToPlay.Play(ConfigManager.GlobalManager.SfxVolume, 0f, 0f);
            return soundToPlay.Duration;
        } 




        protected Direction m_facingDirection;
        public Direction FacingDirection
        { 
            get { return m_facingDirection; }
            set 
            {
                m_facingDirection = value;
                RotationAngle = DirectionRotationAngles[(int)value];
            }
        }

        public static readonly float[] DirectionRotationAngles = new float[(int)Direction.ValueType.NumDirections] { 
            (float)Math.PI / 2f,
            (float)Math.PI * 5f / 6f,
            (float)Math.PI *-5f / 6f,
            (float)-Math.PI / 2f,
            (float)-Math.PI / 6f,   
            (float)Math.PI / 6f
        };

 


        /// When Created, the unit is not visible or clickable until placed on a Map.
        /// That way it can be part of an army before the game starts.  
        protected Unit(
            UnitType type,
            String name, 
            Texture2D texture,
            MapTemplate moveTemplate, 
            MapTemplate targetTemplate, 
            MapTemplate attackTemplate,
            HexEffect attackEffect, 
            int HP, 
            int maxRecharge,
            SoundEffect sfxSelected,
            SoundEffect sfxFire) 
            : base(texture, Rectangle.Empty, Color.White, 1f, 0f, Vector2.Zero, 0f)
        {
            TypeOfUnit = type;
            Name = name;
            MoveTemplate = moveTemplate;

            // Allow movement through allied units.
            if (MoveTemplate as RangeTemplate != null)
            { (MoveTemplate as RangeTemplate).UnitWhoseAlliesDontBlockMovement = this; }


            TargetTemplate = targetTemplate;
            AttackTemplate = attackTemplate;
            AttackEffect = attackEffect; 


            MaxHP = HP;
            CurrentHP = HP;
            MaxRechargeTime = maxRecharge;
            CurrentRechargeTime = 0;

            IsCommander = false;

            DrawOrigin = new Vector2(texture.Height / 2, texture.Width / 2);
            Visible = false;
            Enabled = false;

            SfxSelected.Add(sfxSelected);
            SfxFire.Add(sfxFire);
        }



        /// <summary>
        /// PlaceOnMap places the unit on a certain hex on a certain map and 
        /// sets up the display properties of the unit reletive to that hex.
        /// </summary>
        public bool PlaceOnMap(Hex hex)
        {
            return PlaceOnMap(hex, this.FacingDirection);
        }
        public bool PlaceOnMap(Map map, Point mapCoordinates, Direction facingDirection)
        {
            Hex destination = map.GetHex(mapCoordinates);
            return PlaceOnMap(destination, facingDirection);
        }
        public bool PlaceOnMap(Hex destination, Direction facingDirection)
        {
            if (!destination.IsValidDestination)
                return false;

            //DrawOrigin = new Vector2(Hex.HEX_SIZE / 2, Hex.HEX_SIZE / 2);
            DrawOrigin = new Vector2(Texture.Height / 2, Texture.Width / 2);

            destination.Unit = this;
            Visible = true;
            Enabled = true;
            DisplayRect = destination.DisplayRect;
            FacingDirection = facingDirection;
            
            return true;
        }


        Rectangle getPortionOfDisplayArea(float X, float Y, float Width, float Height)
        {
            return new Rectangle(
                        (int)(Position.X - (DrawOrigin.X * Scale.X) + (Texture.Width * Scale.X * X)),
                        (int)(Position.Y - (DrawOrigin.Y * Scale.Y) + (Texture.Height * Scale.Y * Y)),
                        (int)(Texture.Width * Scale.X * Width),
                        (int)(Texture.Height * Scale.Y * Height));
        }


        const int RECHARGE_NUM_PIXELS = 25;

        ///override the draw function in order to also show HP and recharge data on the unit itself.
        public override void Draw(SpriteBatch spriteBatch, GameTime time)
        {
            base.Draw(spriteBatch, time);

            // Draw the HP Bar
            Rectangle MaxBar = getPortionOfDisplayArea(.2f, .83f, .6f, .07f);
            Rectangle FrameBar = MaxBar; FrameBar.Inflate(1, 1);
            Rectangle CurrentBar = MaxBar;
            CurrentBar.Width = (CurrentBar.Width * CurrentHP) / MaxHP;
            spriteBatch.Draw(TextureStore.Get(TexId.hp_bar_white), FrameBar, Color.Black);
            spriteBatch.Draw(TextureStore.Get(TexId.hp_bar_white), MaxBar, Color.Red);
            spriteBatch.Draw(TextureStore.Get(TexId.hp_bar_white), CurrentBar, Color.Green);

            // Draw the Commander Star
            if (IsCommander)
            {
                Rectangle StarRect = DisplayRect;
                spriteBatch.Draw(TextureStore.Get(TexId.commander_star),
                    new Rectangle(StarRect.Center.X - StarRect.Width / 6,
                        StarRect.Top + (StarRect.Height * 2) / 3,
                        StarRect.Width / 3, StarRect.Height / 3),
                    Color.White);
            }

            // Draw the recharge number (if any)
            if (CurrentRechargeTime > 0)
            {
                Rectangle rechargeDisplayRect = getPortionOfDisplayArea(.55f, .05f, .5f, .5f);
                Rectangle sourceRect = new Rectangle(
                    RECHARGE_NUM_PIXELS * (CurrentRechargeTime-1),
                    0,
                    RECHARGE_NUM_PIXELS,
                    TextureStore.Get(TexId.recharge_numbers).Height);

                spriteBatch.Draw(TextureStore.Get(TexId.recharge_numbers),
                    rechargeDisplayRect, sourceRect, new Color(0,255,0));
            }
        }
    }
}
